package oboe.controllers.debugConsole;

import com.adobe.serialization.json.JSON;
import com.adobe.utils.StringUtil;

import flash.display.DisplayObject;
import flash.net.FileReference;
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;

import oboe.controllers.jml.JMLController;
import oboe.controllers.jml.helpers.JMLButton;
import oboe.controllers.jml.helpers.JMLNode;
import oboe.controllers.jml.helpers.list.TextList;
import oboe.controllers.jml.helpers.window.JMLWindow;
import oboe.core.Oboe;

class DebugConsole extends JMLController
{
private var styleJML:Object =
	{	
	
	};

private var structureJML:Array =
	[
	{"@pipe":"onToggleDebugConsole"},
	{"@pipe":"onCloseWindow"},
	{"@pipe":"onClickSnapshotJSON"},
	{"@pipe":"onClickSnapshotMM"},
	
	{
		"Sprite:btnDebugToggle" : 
		{
		"y":20,
		
		"@call":
		[
			{"graphics.beginFill":[16777215, 0.5]}, // 11579568, 0.1]},
			{"graphics.drawCircle":[5, 10, 25]},
			{"graphics.endFill":[]}
		],

		"@callback": ["click", "signalUp", "onToggleDebugConsole"],
		
		"mouseChildren" : false,
		"mouseEnabled" : true,
		
		"Label:lblDebugToggle" :
		{
			"text":"ODC"
		}
		
		}
	},
	
	{
		"Window:DebugConsole":
		{
		"@pipe:1":"onCloseWindow",
		"@pipe:2":"onClickSnapshotMM",
		"@pipe:3":"onClickSnapshotJSON",
		"title":"Oboe Debug Console",
		"width":400,
		"height":310,
		"visible":false,
		"showResizeButton":false,
		
		"Button:SnapshotJSON":
		{
			"x":5,
			"y":25,
			"width":100,
			"height":25,
			"text":"JSON Snapshot"
		},
		"Button:SnapshotMM":
		{
			"x":110,
			"y":25,
			"width":100,
			"height":25,
			"text":"MM Snapshot"
		},
		"TextList:Content":
		{
			"x":5,
			"y":75,
			"width":390,
			"height":310-80,
			"showVBar":true,
			"showHBar":true
		}
		}
	}
	];


public function new()
{
	super( styleJML );
	this.addControllerType("Window", JMLWindow);
	this.addDisplayObjectType("Button", JMLButton);
	this.addDisplayObjectType("TextList", TextList);
	this.loadStructure( structureJML );
	this.content = this.getNodeFromId("Content").getAsset() as TextList;
}

private var acc:Float = 0;
private var content:TextList;
private var varreg:Object;
public function slot_onTick( time:Float ):Void
{
	var disp:DisplayObject = this.getDisplayObject();
	disp.parent.setChildIndex( disp, disp.parent.numChildren-1 );
	acc += time;
	if( acc >= 1 )
	{
	if( !this.varreg )
	{
		this.varreg = {};
	}
	for( var val:String in Oboe.flags )
	{
		if( val in varreg )
		{
		content.setItem( varreg[val], val + " : " + Oboe.flags[val] );
		}
		else
		{
		varreg[val] = content.pushItem( val + " : " + Oboe.flags[ val ] );
		}
	}
	}
}

public function slot_onCloseWindow():Void
{
	this.slot_onToggleDebugConsole();
}

public function slot_onToggleDebugConsole():Void
{
	var w:JMLNode = this.getNodeFromId("DebugConsole");
	var b:JMLNode = this.getNodeFromId("btnDebugToggle");
	var t:Bool = !w.getProperty("visible");
	w.slot_setProperty("visible", t);
	b.slot_setProperty("visible", !t);
}

private var ss:FileReference;
private var hit:Dictionary;
private var clouds:Array = 
	[
	"E0E0E0",
	"D0D0D0",
	"C0C0C0",
	"B0B0B0",
	"A0A0A0",
	"B0B0B0",
	"C0C0C0",
	"D0D0D0",
	"E0E0E0"
	];
public function slot_onClickSnapshotJSON():Void
{
	ss = new FileReference();
	hit = new Dictionary();
	hit[ this ] = true;
	var data:Object = JSON.encode( snapshot( Oboe.root ) );
	ss.save( data, "snapshot.json" );
	hit = null;
}

public function slot_onClickSnapshotMM():Void
{
	ss = new FileReference();
	hit = new Dictionary();
	hit[ this ] = true;
	var data:String = "<map version=\"0.9.0\"><node TEXT=\"StateMachine\">" + this.genMM( snapshot( Oboe.root ), true, 0 ) + "</node></map>";
	ss.save( data, "snapshot.mm" );
	hit = null;
}

private function genMM( obj:Object, pos:Bool, depth:Int, icon:String = "" ):String
{
	var acc:String = "";
	var tag:String;
	var closeTagS:String;
	var closeTagM:String;
	if( pos )
	{
	tag = "<node POSITION=\"right\" ";
	}
	else
	{
	tag = "<node ";
	}
	
	if( icon != "" )
	{
	closeTagS = "><icon BUILTIN=\"" + icon + "\"/></node>";
	closeTagM = "<icon BUILTIN=\"" + icon + "\"/></node>";
	}
	else
	{
	closeTagS = "/>";
	closeTagM = "</node>";
	}
	
	if( obj is String || obj is int || obj is Boolean || obj is Number || obj == null )
	{
	acc += tag + "TEXT=\"" + obj + "\" " + closeTagS;
	}
	else if( obj is Array )
	{
	for each( var aval:Object in obj )
	{
		if( aval is String || aval is int || aval is Boolean || aval is Number || aval == null )
		{
		acc += genMM( aval, false, depth, icon );
		}
		else
		{
		acc += tag + "TEXT=\"" + aval.constructor.toString() + "\">" + genMM( aval, false, depth, icon ) + closeTagM;
		}
	}
	}
	else if( "isSigSlotInfo" in obj )
	{
	var match:Function = function( key:String, icon:String, obj:Object ):Void
	{
		acc += genMM( obj[key], false, depth, icon );
	};
	
	match( "children", "", obj );
	match( "slots", "bell", obj );
	match( "pipes", "up", obj );
	match( "meta",  "idea", obj );
	}
	else
	{
	for( var key:String in obj )
	{
		var val:Object = obj[key];
		var altkey:String = key;
		if( "__instanceCount" in val )
		{
		altkey = key + " (x" + val.__instanceCount + ")";
		}
		acc += tag;
		acc += "FOLDED=\"true\" ";
		acc += "TEXT=\"" + altkey + "\">" + genMM( val, false, depth+1, icon );
		acc += "<cloud COLOR=\"#" + clouds[ depth%clouds.length ] + "\"/>";
		acc += closeTagM;
	}
	}
	return acc;
}

private function getMetaRec( fc:Class, ret:Array, index:Object ):Void
{
	var dt:XMLList = describeType(fc)..constant;
	var len:Int = dt.length();
	for( var i:Int=0; i<len; ++i )
	{
	var name:String = dt.@name[i];
	if( StringUtil.beginsWith(name, "SIG_") && !(fc[name] in index) )
	{
		ret.push( fc[ name ] );
	}
	}
}

private function getMeta( o:Object, index:Object ):Array
{
	var ret:Array = [];
	var fc:Class = o.constructor;
	var dt:XMLList = describeType(fc).factory.extendsClass;
	var len:Int = dt.length();
	getMetaRec( fc, ret, index );
	for( var i:Int=0; i<len; ++i )
	{
	var name:String = dt.@type[i].toString().replace(/::/g, ".");
	var t:Class = getDefinitionByName( name ) as Class;
	getMetaRec( t, ret, index );
	}
	
	dt = describeType( o )..variable;
	len = dt.length();
	for( i=0; i<len; ++i )
	{
	name = dt.@name[i];
	if( StringUtil.beginsWith(name, "SIG_") && !(o[name] in index) )
	{
		ret.push( o[ name ] );
	}
	}
	return ret;
}

private function snapshot( c:Object ):Object
{
	var ret:Object = { isSigSlotInfo:true };
	hit[ c ] = true;
	var slots:Object = {};
	var pipes:Object = {};
	var children:Object = {};
	var meta:Object = {};
	
	c.message("_getSigSlotInfo", children, pipes, slots, meta );
	
	ret.meta = getMeta( c, meta );

	var childClasses:Dictionary = new Dictionary(true);

	ret.children = {};
	for each( var entries:Array in children )
	{
	for each( var child:Object in entries )
	{
		if( child == this )
		{
		continue;
		}
		if( child.constructor in childClasses )
		{
		if( !("__instanceCount" in ret.children[ child ] ) )
		{
			ret.children[ child ].__instanceCount = 1;
		}
		ret.children[ child ].__instanceCount++;
		}
		else
		{
		ret.children[child] = snapshot( child );
		childClasses[ child.constructor ] = true;
		}
	}
	}
	
	ret.pipes = [];
	for each( var pipe:String in pipes )
	{
	ret.pipes.push( pipe ); 
	}

	for each( var sig:String in meta )
	{
	ret.meta.push( sig ); 
	}
	
	ret.slots = [];
	for each( var slot:String in slots )
	{
	ret.slots.push( slot ); 
	}
	
	return ret;
}
}